home *** CD-ROM | disk | FTP | other *** search
/ Mixa 155: Dogs / MIXA 155: Dogs.iso / pc / Viewer / BROWSER(W) / フィルタ / PDF / LIB / pdfopt.ps < prev    next >
Encoding:
Text File  |  2002-10-29  |  29.8 KB  |  1,043 lines

  1. %    Copyright (C) 2000 Aladdin Enterprises.  All rights reserved.
  2. % This software is licensed to a single customer by Artifex Software Inc.
  3. % under the terms of a specific OEM agreement.
  4.  
  5. % $RCSfile: pdfopt.ps,v $ $Revision: 1.1 $
  6. % PDF linearizer ("optimizer").
  7.  
  8. .currentglobal true .setglobal
  9. /pdfoptdict 200 dict def
  10. pdfoptdict begin
  11.  
  12. % This linearizer is designed for simplicity, not for performance.
  13. % See the main program (the last procedure in the file) for comments
  14. % describing the main processing sequence.
  15.  
  16. % ---------------- Utilities ---------------- %
  17.  
  18. % ------ Data structures ------ %
  19.  
  20. % Distinguish dictionaries, arrays, and everything else.
  21. /ifdaelse {        % <obj> <dictproc> <arrayproc> <otherproc> ifdaelse -
  22.   3 index type dup /dicttype eq {
  23.     pop pop pop
  24.   } {
  25.     dup /arraytype ne exch /packedarraytype ne and {
  26.       exch
  27.     } if pop exch pop
  28.   } ifelse exec
  29. } bind def
  30.  
  31. % Implement dynamically growable arrays using a dictionary.
  32. /darray {        % <size> darray <darray>
  33.   dict
  34. } bind def
  35. /dadd {            % <darray> <value> dadd -
  36.   1 index length exch put
  37. } bind def
  38. /daforall {        % <darray> <proc> daforall -
  39.   /exch cvx /get cvx 3 -1 roll /exec cvx 5 packedarray cvx
  40.   0 1 2 index 0 get length 1 sub 4 -1 roll for
  41. } bind def
  42. /dacontents {        % <darray> dacontents <array>
  43.   [ exch { } daforall ]
  44. } bind def
  45. /dacontstring {        % <darray> dacontstring <string>
  46.   0 1 index { exch pop length add } forall string
  47.   dup /NullEncode filter
  48.             % Stack: darray str filter
  49.   3 -1 roll { 1 index exch writestring } daforall
  50.   closefile
  51. } bind def
  52.  
  53. % Force an object, mapping it if it is a reference.
  54. /omforcenew {        % <obj> omforce <obj'> <notseen>
  55.   dup oforce 2 copy eq { pop true } { exch 0 get omapnew exch pop } ifelse
  56. } bind def
  57. /omforce {        % <obj> omforce <obj'>
  58.   omforcenew pop
  59. } bind def
  60. /omget {        % <dict|array> <key> omget <obj>
  61.   get omforce
  62. } bind def
  63. % Visit an entire tree.
  64. /omvisit {        % <obj> omvisit -
  65.   omforcenew {
  66.     { { omvisit omvisit } forall }
  67.     { { omvisit } forall }
  68.     { pop }
  69.     ifdaelse
  70.   } {
  71.     pop
  72.   } ifelse
  73. } bind def
  74.  
  75. % Collect the list of currently mapped object numbers, in order.
  76. /omapped {        % - omapped <obj#s>
  77.   RMap length array RMap {
  78.     2 index 3 1 roll 1 sub exch put
  79.   } forall
  80. } bind def
  81.  
  82. % Collect the list of object numbers passed to omap by a procedure.
  83. /visited {        % <proc> visited <obj#s>
  84.   false currentomap 2 .execn
  85.   omapped exch setomap
  86. } bind def
  87.  
  88. % ------ Output ------ %
  89.  
  90. % Provide a framework for closure-based streams.
  91. .currentglobal false .setglobal
  92. userdict /clostreams 20 dict put    % stream -> [data endproc]
  93. .setglobal
  94. % Create a closure-based stream.
  95. /clostream {        % <data> <proc> <endproc> clostream <stream>
  96.   2 index 3 -1 roll /exec load 3 packedarray cvx
  97.   /NullEncode filter
  98.         % Stack: data endproc stream
  99.   clostreams 1 index 5 -2 roll 2 array astore put
  100. } bind def
  101. % Close a closure-based stream.
  102. /closend {        % <stream> closend <result>
  103.   dup closefile clostreams exch
  104.   2 copy get 3 1 roll undef aload pop exec
  105. } bind def
  106.  
  107. % Implement in-memory output streams.
  108. /msproc {        % <data> <more> <accum> msproc <scratch>
  109.   3 -1 roll dadd { 100 string } { () } ifelse
  110. } bind def
  111. /mstream {        % - mstream <mstream>
  112.   10 darray {msproc} {dacontstring} clostream
  113. } bind def
  114. /mcontents {        % <mstream> mcontents <string>
  115.   closend
  116. } bind def
  117.  
  118. % Implement a stream that only keeps track of its position.
  119. % (All streams should do this, but the PLRM doesn't require it.)
  120. /posbuf 100 string def
  121. /posproc {        % <data> <more> <accum> posproc <scratch>
  122.   0 2 copy get 5 -1 roll length add put
  123.   pop //posbuf
  124. } bind def
  125. /postream {        % - postream <postream>
  126.   [0] {posproc} {0 get} clostream
  127. } bind def
  128. /poslength {        % <postream> poslength <pos>
  129.   closend
  130. } bind def
  131.  
  132. % Implement streams with variable-bit-width data.
  133. % Note that these are dictionary objects, not stream objects.
  134. /bitstream {        % <stream> bitstream <bstream>
  135.   4 dict begin /S exch def /N 8 def /B 0 def
  136.   currentdict end
  137. } bind def
  138. /bitwrite {        % <bstream> <value> <width> bitwrite -
  139.   PDEBUG { ( ) print 1 index =only (:) print dup = } if
  140.   3 -1 roll begin
  141.   N exch sub dup 0 ge {
  142.     /N exch def N bitshift B add
  143.   } {
  144.     2 copy bitshift B add S exch write
  145.             % Stack: value -left
  146.     { 8 add dup 0 ge { exit } if
  147.       2 copy bitshift 255 and S exch write
  148.     } loop
  149.     /N 1 index def bitshift 255 and
  150.   } ifelse /B exch def
  151.   end
  152. } bind def
  153. /bitflush {        % <bstream> bitflush -
  154.   begin N 8 ne { S B write /B 0 def /N 8 def } if end
  155. } bind def
  156.  
  157. % Capture OFile output on the temporary file, in memory, or just as a length.
  158. /totemp {        % <proc> totemp <start> <end>
  159.   TFile fileposition OFile
  160.   /OFile TFile def 3 .execn
  161.   /OFile exch def
  162.   TFile fileposition
  163. } bind def
  164. /tomemory {        % <proc> tomemory <string>
  165.   OFile /OFile mstream def 2 .execn
  166.   OFile mcontents exch /OFile exch def
  167. } bind def
  168. /tolength {        % <proc> tolength <string>
  169.   OFile /OFile postream def 2 .execn
  170.   OFile poslength exch /OFile exch def
  171. } bind def
  172.  
  173. % Copy a range of bytes from TFile to OFile.
  174. /copyrange {        % <start> <end> copybytes -
  175.   TFile 2 index setfileposition
  176.   exch sub 1024 string exch {
  177.         % Stack: buf left
  178.     2 copy 1 index length .min 0 exch getinterval
  179.     TFile exch readstring pop OFile exch writestring
  180.     1 index length sub dup 0 le { exit } if
  181.   } loop pop pop
  182. } bind def
  183.  
  184. % Pad with blanks to a specified position.
  185. /padto {        % <pos> padto -
  186.   OFile fileposition sub
  187.   dup 0 lt {
  188.     (ERROR: file position incorrect by ) print =
  189.     /padto cvx /rangecheck signalerror
  190.   } {
  191.     { ( ) ows } repeat
  192.   } ifelse
  193. } bind def
  194.  
  195. % ---------------- Read objects into memory ---------------- %
  196.  
  197. /touch {        % <object> touch -
  198.   {
  199.     { touch touch } forall
  200.   } {
  201.     dup xcheck {
  202.         % Executable array, must be an indirect object.
  203.       dup 0 get resolved? { pop pop } { oforce touch } ifelse
  204.     } {
  205.       { touch } forall
  206.     } ifelse
  207.   } {
  208.     pop
  209.   } ifdaelse
  210. } bind def
  211.  
  212. % ---------------- Replace references with referents ---------------- %
  213.  
  214. /replaceable? {        % <value> replaceable? <bool>
  215.   dup type /integertype eq exch xcheck not and
  216. } bind def
  217. /replacement {        % <obj|ref> replacement <obj'>
  218.   dup oforce dup replaceable? { exch } if pop
  219. } bind def
  220.  
  221. /replacerefs {        % <object> replacerefs <object>
  222.   {
  223.     dup {
  224.       2 index 2 index undef
  225.       exch replacement exch replacement
  226.       2 index 3 1 roll put
  227.     } forall
  228.   } {
  229.     0 1 2 index length 1 sub {
  230.       1 index exch 2 copy get replacement put
  231.     } for
  232.   } {
  233.   } ifdaelse
  234. } bind def
  235.  
  236. /replaceReferences {    % - replaceReferences -
  237.   Objects { replacerefs pop } lforall
  238.         % Delete replaced objects.
  239.   0 1 Objects llength 1 sub {
  240.     Objects 1 index lget replaceable? {
  241.       PDEBUG { (Deleting ) print dup = } if
  242.       Generations 1 index 0 lput
  243.     } if pop
  244.   } for
  245. } bind def
  246.  
  247. % ---------------- Create new objects ---------------- %
  248.  
  249. /createObjects {    % [<obj>...] createObjects <firstobj#>
  250.   Objects llength dup
  251.   dup 3 index length add growPDFobjects
  252.         % Stack: objects objn objn
  253.   3 1 roll exch {
  254.     Objects 2 index 3 -1 roll lput
  255.     Generations 1 index 1 lput
  256.     1 add
  257.   } forall pop
  258. } bind def
  259.  
  260. % ---------------- Propagate attributes ---------------- %
  261.  
  262. /nopropattrs <<
  263.     % Never propagate these.
  264.   /Type dup /Kids dup /Count dup /Parent dup
  265.     % Handle Resources specially.
  266.   /Resources dup
  267. >> def
  268.  
  269. % Merge Resources.
  270. /mergeres {        % <fromdict> <todict> mergeres -
  271.         % Values in todict take priority over fromdict.
  272.   1 index /Resources .knownget {
  273.     1 index /Resources .knownget {
  274.         % Stack: fromdict todict fromres tores
  275.       exch oforce exch oforce
  276.         % todict's Resources may be shared, so make a copy.
  277.       dup length dict .copydict
  278.       exch {
  279.         % Stack: fromdict todict tores' fromkey fromvalue
  280.     2 index 2 index knownoget {
  281.         % Stack: fromdict todict tores' fromkey fromvalue tovalue
  282.       exch oforce exch
  283.         % ProcSet is an array, other types are dictionaries.
  284.       dup type /dicttype eq {
  285.         % Dictionary, not ProcSet.
  286.         exch dup length 2 index length add dict .copydict .copydict
  287.       } {
  288.         % Array or packed array, ProcSet.
  289.         % Use dictionaries to do the merge.
  290.         dup length 2 index length add dict begin
  291.         exch { dup def } forall { dup def } forall
  292.         mark currentdict end { pop } forall .packtomark
  293.       } ifelse
  294.     } if
  295.     2 index 3 1 roll put
  296.       } forall
  297.     } if /Resources exch put pop
  298.   } {
  299.     pop pop
  300.   } ifelse
  301. } bind def
  302.  
  303. % Merge attributes other than Resources.
  304. /mergeattrs {        % <fromdict> <todict> mergeattrs <fromdict> <todict>
  305.         % Values in todict take priority over fromdict.
  306.   1 index {
  307.         % Stack: fromdict todict fromkey fromvalue
  308.     //nopropattrs 2 index known {
  309.       pop pop
  310.     } {
  311.       2 index 2 index known { pop pop } { 2 index 3 1 roll put } ifelse
  312.     } ifelse
  313.   } forall
  314. } bind def
  315.  
  316. % Propagate attributes to a subtree.
  317. /proppage {        % <attrs> <subtree> proppage -
  318.         % We should be able to tell when we reach a leaf
  319.         % by finding a Type unequal to /Pages.  Unfortunately,
  320.         % some files distributed by Adobe lack the Type key
  321.         % in some of the Pages nodes!  Instead, we check for Kids.
  322.   dup /Kids knownoget {
  323.         % Accumulate inherited values.
  324.     3 1 roll
  325.         % Stack: kids attrs pagesnode
  326.     dup length dict .copydict mergeattrs
  327.     dup 3 1 roll mergeres
  328.     exch { oforce 1 index exch proppage } forall pop
  329.   } {
  330.         % Merge inherited values into the leaf.
  331.     mergeattrs mergeres
  332.   } ifelse
  333. } bind def
  334.  
  335. % Propagate attributes to all pages.
  336. /propagateAttributes {    % - propagateAttributes -
  337.   0 dict Trailer /Root oget /Pages oget proppage
  338. } bind def
  339.  
  340. % ---------------- Identify document-level objects ---------------- %
  341.  
  342. /identifyDocumentObjects {    % - identifyDocumentObjects <obj#s>
  343.   {
  344.     Trailer /Root omget
  345.     dup /PageMode .knownget { omvisit } if
  346.     dup /OpenAction .knownget { omvisit } if
  347.     Trailer /Encrypt .knownget { omvisit } if
  348.     dup /Threads .knownget {
  349.       omforce { omforce } forall
  350.     } if
  351.     dup /AcroForm .knownget { omvisit } if
  352.     pop
  353.   } visited
  354. } bind def
  355.  
  356. % ---------------- Identify the objects of each page ---------------- %
  357.  
  358. /identifyfont {        % <fontref> identifyfont -
  359.   omforce {
  360.     exch /FontDescriptor eq {
  361.       omforce dup /Flags .knownget { 32 and 0 ne } { false } ifelse
  362.       exch {
  363.     exch dup dup /FontFile eq exch /FontFile2 eq or
  364.     exch /FontFile3 eq or 2 index and {
  365.       fontfiles exch dadd
  366.     } {
  367.       omvisit
  368.     } ifelse
  369.       } forall pop
  370.     } {
  371.       omvisit
  372.     } ifelse
  373.   } forall
  374. } bind def
  375.  
  376. /identifyPageObjects {    % <extra> <page#> identifyPageObjects <obj#s>
  377.   pdffindpageref
  378.   4 dict begin
  379.   /images 10 darray def
  380.   /fontfiles 10 darray def
  381.   {
  382.     omforce
  383.         % Stack: extra page
  384.         % Visit any extra objects if applicable.
  385.     exch omvisit
  386.         % Visit Annots, if any.
  387.         % We don't try to defer the drawing information.
  388.     dup /Annots .knownget { omvisit } if
  389.         % Visit beads.
  390.     dup /B .knownget { omvisit } if
  391.         % Visit resources dictionaries.
  392.     dup /Resources .knownget {
  393.       omforce dup {
  394.         % Visit the first-level Resource dictionaries.
  395.     omforce pop pop
  396.       } forall {
  397.         % Visit the resources themselves.
  398.         % Skip Image XObjects, and FontFile streams if the
  399.         % FontDescriptor Flags have bit 6 set.
  400.         % We don't try to visit the resources in the order in which
  401.         % the Contents stream(s) reference(s) them.
  402.     exch dup /XObject eq {
  403.       pop oforce {
  404.         dup oforce /Subtype get /Image eq {
  405.           images exch dadd
  406.         } {
  407.           omvisit
  408.         } ifelse pop
  409.       } forall
  410.     } {
  411.       /Font eq {
  412.         oforce { identifyfont pop } forall
  413.       } {
  414.         oforce omvisit
  415.       } ifelse
  416.     } ifelse
  417.       } forall
  418.     } if
  419.         % Visit the Contents stream(s).
  420.     dup /Contents .knownget { omvisit } if
  421.         % Visit Image XObjects.  We don't try to visit them in
  422.         % reference order.
  423.     images { omvisit } daforall
  424.         % Visit FontFile streams.  We don't try to visit them in
  425.         % reference order.
  426.     fontfiles { omvisit } daforall
  427.     pop
  428.   } visited end
  429. } bind def
  430.  
  431. % Identify the objects of the first page.
  432. /identifyFirstPageObjects {    % -identifyFirstPageObjects <obj#s>
  433.   Trailer /Root oget null
  434.   1 index /PageMode knownoget {
  435.     /UseOutlines eq {
  436.       pop dup /Outlines get
  437.     } if
  438.   } if exch pop
  439.   1 identifyPageObjects
  440. } bind def
  441.  
  442. % Identify the non-shared objects of the other pages, and the shared objects.
  443. /identifyOtherPageObjects {    % - identifyOtherPageObjects [<pageobj#s> ...]
  444.                 %   <sharedobj#s>
  445.   4 dict begin
  446.   /marks lstring Objects llength lgrowto def
  447.         % Mark document-level and first page objectsw.
  448.   [CatalogNs FirstPageNs] {
  449.     { marks exch 255 lput } forall
  450.   } forall
  451.         % Collect objects of other pages and identify sharing.
  452.   [ 2 1 pdfpagecount { null exch identifyPageObjects } for ]
  453.   dup {
  454.     { marks exch 2 copy lget 1 add 254 min lput } forall
  455.   } forall
  456.   [ exch {
  457.     [ exch {
  458.       marks 1 index lget 1 ne { pop } if
  459.     } forall ]
  460.   } forall ]
  461.   [ 1 1 marks llength 1 sub {
  462.     marks 1 index lget dup 1 le exch 255 eq or { pop } if
  463.   } for ]
  464.   end
  465. } bind def
  466.  
  467. % Identify objects not associated with any page.
  468. /identifyNonPageObjects {    % - identifyNonPageObjects <obj#s>
  469.   4 dict begin
  470.   /marks lstring Objects llength lgrowto def
  471.   [[[LPDictN PHSN] CatalogNs FirstPageNs SharedNs] OtherPageNs] {
  472.     { { marks exch 1 lput } forall } forall
  473.   } forall
  474.     %****** PUT THESE IN A REASONABLE ORDER ******
  475.   [ 1 1 Objects llength 1 sub {
  476.     marks 1 index lget 0 eq {
  477.       Generations 1 index lget 0 eq { pop } if
  478.     } {
  479.       pop
  480.     } ifelse
  481.   } for ]
  482. } bind def
  483.  
  484. % ---------------- Assign object numbers ---------------- %
  485.  
  486. % Assign object numbers to all objects that will be copied.
  487. % Return the first (translated) object number in the First Page xref table.
  488. /assignObjectNumbers {        % - assignObjectNumbers -
  489.   OtherPageNs { { omap pop } forall } forall
  490.   SharedNs { omap pop } forall
  491.   NonPageNs { omap pop } forall
  492.         % Assign object numbers for the First Page xref table last.
  493.   LPDictN omap    % don't pop, this is the return value
  494.   CatalogNs { omap pop } forall
  495.   FirstPageNs { omap pop } forall
  496.   PHSN omap pop
  497. } bind def
  498.  
  499. % ---------------- Create the LPDict ---------------- %
  500.  
  501. % Create the contents of the LPDict.  Free variables:
  502. %   PHSStart PHSEnd FirstPageEnd Xref0Start FileEnd
  503. /createLPDict {            % <phsstart> <phsend> <firstpageend>
  504.                 %   <xref0start> <filelength> createLPDict -
  505.   LPDict
  506.   dup /Linearized 1 put
  507.   dup /L 4 -1 roll put
  508.   dup /T 4 -1 roll put
  509.   dup /E 4 -1 roll put
  510.   dup /H 5 -2 roll 1 index sub 2 array astore put
  511.   dup /O 1 pdffindpageref 0 get omap put
  512.   /N pdfpagecount put
  513. } bind def
  514.  
  515. % ---------------- Adjust object positions ---------------- %
  516.  
  517. /adjustObjectPositions {    % <boundary> <deltabelow> <deltaabove>
  518.                 %   adjustObjectPositions -
  519.     % Objects fall into 4 categories: LPDict, PHS, Catalog, and others.
  520.     % We handle the first two as special cases.
  521.   XRef {
  522.         % Stack: bdy below above key loc
  523.     dup 5 index ge { 2 } { 3 } ifelse index add
  524.     XRef 3 1 roll put
  525.   } forall pop pop pop
  526.   XRef LPDictN omap HeaderLength put
  527.   XRef PHSN omap PHSStart put
  528. } bind def
  529.  
  530. % ---------------- Write the output file ---------------- %
  531.  
  532. % Write objects identified by object number.
  533. /writeobjn {        % <obj#> writeobjn
  534.   Generations 1 index lget pdfwriteobj
  535. } bind def
  536. /writeobjns {        % <obj#s> writeobjns -
  537.   { writeobjn } forall
  538. } bind def
  539.  
  540. % Write the header.
  541. /writePart1 {        % - writePart1 -
  542.   pdfwriteheader
  543. } bind def
  544.  
  545. % Write the linearization parameters dictionary.
  546. /writePart2 {        % - writePart2 -
  547.   LPDictN writeobjn
  548. } bind def
  549.  
  550. % Write the First Page xref table and trailer.
  551. % Free variables: FirstPageXN.
  552. /writePart3 {        % <xrefstart> writePart3 -
  553.   FirstPageXN NObjects 1 add 1 index sub pdfwritexref
  554.   Trailer dup length 1 add dict copy
  555.   dup /Size NObjects 1 add put
  556.   dup /Prev 4 -1 roll put
  557.   pdfwritetrailer
  558.   0 pdfwritestartxref
  559. } bind def
  560.  
  561. % Write the Catalog and other required document-level objects.
  562. % Free variables: CatalogNs.
  563. /writePart4 {        % - writePart4 -
  564.   CatalogNs writeobjns
  565. } bind def
  566.  
  567. % Write the Primary Hint Stream.
  568. /writePart5 {        % - writePart5 -
  569.   PHSN writeobjn
  570. } bind def
  571.  
  572. % Write the First Page's objects.
  573. % Free variables: FirstPageNs.
  574. /writePart6 {        % - writePart6 -
  575.   FirstPageNs writeobjns
  576. } bind def
  577.  
  578. % Write the objects of other pages (Page + non-shared objects).
  579. % Free variables: OtherPageNs.
  580. /writePart7 {        % - writePart7 <lengths>
  581.   [ OtherPageNs {
  582.     OFile fileposition exch
  583.     writeobjns OFile fileposition exch sub
  584.   } forall ]
  585. } bind def
  586.  
  587. % Write the shared objects of other pages.
  588. % Free variables: SharedNs.
  589. /writePart8 {        % - writePart8 -
  590.   SharedNs writeobjns
  591. } bind def
  592.  
  593. % Write the other objects not associated with pages.
  594. % Free variables: NonPageNs.
  595. /writePart9 {        % - writePart9 -
  596.   NonPageNs writeobjns
  597. } bind def
  598.  
  599. % Write the main xref table and trailer.
  600. % Free variables: FirstPageXN.
  601. /writePart11xref {    % writePart11 -
  602.   0 FirstPageXN pdfwritexref
  603. } bind def
  604. /writePart11rest {    % <part3start> writePart11rest -
  605.   << /Size FirstPageXN >> pdfwritetrailer
  606.   pdfwritestartxref
  607. } bind def
  608.  
  609. % ---------------- Write hint tables ---------------- %
  610.  
  611. /bitsneeded {        % <maxvalue> bitsneeded <#bits>
  612.   0 exch { dup 0 eq { pop exit } if exch 1 add exch 2 idiv } loop
  613. } bind def
  614.  
  615. % Find the start and end of objects in the output.
  616. /omstart {        % <obj#> omstart <pos>
  617.   PDEBUG { (start\() print dup =only } if
  618.   omap XRef exch get
  619.   PDEBUG { (\)=) print dup = } if
  620. } bind def
  621. /omend {        % <obj#> omend <pos>
  622.     % The end of an object is the start of the next object.
  623.     % The caller must be sure that this object is not the last one
  624.     % in part 9.
  625.   PDEBUG { (end\() print dup =only } if
  626.   omap 1 add
  627.     % Check that the requested object wasn't the last one in part 6:
  628.     % the next object in the output file is the first in part 7.
  629.   PHSN omap 1 index eq { pop 1 } if
  630.   XRef exch get
  631.   PDEBUG { (\)=) print dup = } if
  632. } bind def
  633. /omlength {        % <obj#> omlength <length>
  634.   dup omend exch omstart sub
  635. } bind def
  636.  
  637. % Find the Contents of a page.
  638. /contentsobjects {    % <pagedict> contentsobjects <firstobj#> <lastobj#>
  639.   /Contents get
  640.   dup oforce dup type /dicttype eq {
  641.     pop dup
  642.   } {
  643.     dup 0 get exch dup length 1 sub get
  644.   } ifelse
  645.   exch 0 get exch 0 get
  646. } bind def
  647. /contentsstart {    % <pagedict> contentsstart <pos>
  648.   contentsobjects pop omstart
  649. } bind def
  650. /contentslength {    % <pagedict> contentslength <length>
  651.   contentsobjects omend exch omstart sub
  652. } bind def
  653.  
  654.  
  655. /writePageOffsetHints {
  656.   PDEBUG { /writePageOffsetHints == } if
  657.   20 dict begin
  658.   /bits OFile bitstream def
  659.   /bwn { bits 3 1 roll bitwrite } def
  660.  
  661.         % Least number of objects in a page:
  662.   FirstPageNs length OtherPageNs { length .min } forall
  663.   /minnop 1 index def 32 bwn
  664.         % Location of first page's Page object:
  665.   FirstPageNs 0 get omap XRef exch get 32 bwn
  666.         % Bits needed to represent greatest # of objects in a page:
  667.   FirstPageNs length OtherPageNs { length .max } forall
  668.   minnop sub bitsneeded /maxnopbits 1 index def 16 bwn
  669.         % Least length of a page:
  670.   FirstPageLength OtherPageLengths { .min } forall
  671.   /minpl 1 index def 32 bwn
  672.         % Bits needed to represent the greatest page length:
  673.   FirstPageLength OtherPageLengths { .max } forall
  674.   minpl sub bitsneeded /maxplbits 1 index def 16 bwn
  675.         % Least start of Contents offset:
  676.   0        % (Acrobat requires that this be 0.)
  677.   /minsco 1 index def 32 bwn
  678.         % Bits needed to represent the greatest start of Contents
  679.         % offset:
  680.   0        % (Acrobat requires that this be 0?!)
  681.   /maxscobits 1 index def 16 bwn
  682.         % Least contents length:
  683.   FirstPageNs 0 get Objects exch lget contentslength
  684.   OtherPageNs { 0 get Objects exch lget contentslength .min } forall
  685.   /mincl 1 index def 32 bwn
  686.         % Bits needed to represent the greatest Contents length:
  687.   FirstPageNs 0 get Objects exch lget contentslength
  688.   OtherPageNs { 0 get Objects exch lget contentslength .max } forall
  689.   mincl sub bitsneeded
  690.   /maxclbits 1 index def 16 bwn
  691.         % Bits needed to represent the greatest number of Shared
  692.   0        % Object references (we don't report any):
  693.   /maxsorbits 1 index def 16 bwn
  694.         % Bits needed to identify a Shared Object (we don't
  695.   0        % report any):
  696.   /sobits 1 index def 16 bwn
  697.         % Bits needed to represent numerator of fraction (only needed
  698.   0        % for Shared Object references, which we don't report):
  699.   /numfbits 1 index def 16 bwn
  700.         % Denominator of fraction (only needed for Shared Object
  701.         % references, which we don't report):
  702.   255    % arbitrary
  703.   /denf 1 index def 16 bwn
  704.  
  705.         % Number of objects in pages:
  706.   FirstPageNs length minnop sub maxnopbits bwn
  707.   OtherPageNs {
  708.     length minnop sub maxnopbits bwn
  709.   } forall
  710.  
  711.         % Total length of pages in bytes;
  712.   FirstPageLength minpl sub maxplbits bwn
  713.   OtherPageLengths {
  714.     minpl sub maxplbits bwn
  715.   } forall
  716.  
  717.         % Number of shared objects referenced from page:
  718.         % (Currently we don't report this.)
  719.   OtherPageNs length 1 add { 0 maxsorbits bwn } repeat
  720.  
  721.         % Since there are no shared object references,
  722.         % the next two sections are empty.
  723.  
  724.         % Contents offsets:
  725.   [FirstPageNs OtherPageNs aload pop] {
  726.     0 get Objects exch lget contentsstart minsco sub maxscobits bwn
  727.   } forall
  728.  
  729.         % Contents lengths:
  730.   [FirstPageNs OtherPageNs aload pop] {
  731.     0 get Objects exch lget contentslength mincl sub maxclbits bwn
  732.   } forall
  733.  
  734.   bits bitflush end
  735. } bind def
  736.  
  737. /writeSharedObjectHints {
  738.   PDEBUG { /writeSharedObjectHints == } if
  739.   20 dict begin
  740.   /bits OFile bitstream def
  741.   /bwn { bits 3 1 roll bitwrite } def
  742.  
  743.         % Currently we use the Shared Object hint table only for
  744.         % the objects in the first page, which are all treated as
  745.         % "shared" objects.
  746.  
  747.         % Object number of first object in Shared Objects section
  748.         % (not currently used):
  749.   0 32 bwn
  750.         % Location of first object in Shared Objects section
  751.         % (not currently used):
  752.         % **** Acrobat always sets this to 16.
  753.   16 32 bwn
  754.         % Number of Shared Object entries for first page:
  755.   FirstPageNs length 32 bwn
  756.         % Number of Shared Object entries for Shared Objects
  757.         % section (not currently used):
  758.         % **** Acrobat adds in the previous value.
  759.   FirstPageNs length 32 bwn
  760.         % Bits needed to represent the greatest number of objects
  761.         % in a shared object group (always 0, because all groups
  762.         % have only 1 object):
  763.   0 16 bwn
  764.         % Least length of a Shared Object Group in bytes:
  765.   16#7fffffff FirstPageNs { omlength .min } forall
  766.   /minsol 1 index def 32 bwn
  767.         % Bits needed to represent the greatest length of a
  768.         % Shared Object Group:
  769.   0 FirstPageNs { omlength .max } forall
  770.   minsol sub bitsneeded
  771.   /maxsolbits 1 index def 16 bwn
  772.  
  773.         % Lengths of shared object groups:
  774.   FirstPageNs { omlength minsol sub maxsolbits bwn } forall
  775.  
  776.         % MD5 flag:
  777.   0 1 bwn
  778.  
  779.   bits bitflush end
  780. } bind def
  781.  
  782. % ---------------- Main program ---------------- %
  783.  
  784. /tmpprefix (/tmp/) def
  785.  
  786. /pdfOptimize {        % <infile> <outfile> pdfOptimize -
  787.   realtime 3 1 roll
  788.   exch pdfdict begin pdfopenfile dup begin
  789.   40 dict begin
  790.   /IDict exch def
  791.   /OFile exch def
  792.   /starttime exch def
  793.   /now {
  794.     QUIET { pop } { print (, t = ) print realtime starttime sub = flush } ifelse
  795.   } def
  796.   omapinit
  797.   
  798.     % Create and open a temporary file.
  799.  
  800.   { tmpprefix realtime abs =string cvs concatstrings
  801.     mark 1 index status {
  802.       cleartomark pop
  803.     } {
  804.       pop exit
  805.     } ifelse
  806.   } loop
  807.   /TFileName 1 index def
  808.   (w) file /TFile exch def
  809.  
  810.     % Read all objects into memory.
  811.  
  812.   Trailer touch
  813.   (Read objects) now
  814.  
  815.     % Replace indirect references to numbers.  This is needed
  816.     % for the Length of streams, and doesn't hurt anything else.
  817.  
  818.   replaceReferences
  819.   (Replaced references) now
  820.  
  821.     % Create the two new objects: the linearization parameter
  822.     % dictionary, and the Primary Hint Stream.
  823.  
  824.   /LPDict 10 dict def
  825.   /PHS 10 dict cvx def        % executable = stream
  826.   [LPDict PHS] createObjects
  827.   /LPDictN 1 index def 1 add
  828.   /PHSN exch def
  829.  
  830.     % Count the number of objects in the output.
  831.  
  832.   0 0 1 Objects llength 1 sub {
  833.     Generations exch lget 0 ne { 1 add } if
  834.   } for
  835.   /NObjects exch def
  836.   QUIET not { NObjects =only ( objects total) = flush } if
  837.  
  838.     % Propagate inherited attributes down the page tree.
  839.  
  840.   propagateAttributes
  841.   (Propagated attributes) now
  842.  
  843.     % Identify the document-level objects (part 4).
  844.  
  845.   identifyDocumentObjects /CatalogNs exch def
  846.   QUIET not { CatalogNs === flush } if
  847.   (Identified Catalog) now
  848.  
  849.       % Identify the first page's objects (part 6),
  850.     % including the Outlines tree if appropriate.
  851.  
  852.   pdfopencache
  853.   /FirstPageNs identifyFirstPageObjects def
  854.   QUIET not { FirstPageNs === flush } if
  855.   (Identified first page) now
  856.  
  857.     % Identify shared vs. non-shared objects for remaining pages
  858.     % (parts 7 and 8).
  859.  
  860.   identifyOtherPageObjects
  861.   /SharedNs exch def
  862.   /OtherPageNs exch def
  863.   QUIET not { OtherPageNs === flush SharedNs === flush } if
  864.   (Identified other pages) now
  865.  
  866.     % Identify objects not associated with any page (part 9).
  867.  
  868.   /NonPageNs identifyNonPageObjects def
  869.   QUIET not { NonPageNs === flush } if
  870.   (Identified non-pages) now
  871.  
  872.     % Assign final object numbers to all the objects.
  873.     % (The omap is currently empty.)
  874.  
  875.   /FirstPageXN assignObjectNumbers def
  876.   (Assigned objects #s) now
  877.  
  878.     % Write the document-level objects (part 4).
  879.  
  880.   { writePart4 } totemp
  881.   /CatalogTempEnd exch def /CatalogTempStart exch def
  882.   (Wrote Catalog) now
  883.  
  884.     % Write the first page's objects (part 6).
  885.  
  886.   { writePart6 } totemp
  887.   /FirstPageTempEnd exch def /FirstPageTempStart exch def
  888.   (Wrote first page) now
  889.  
  890.     % Write the non-shared objects for other pages (part 7).
  891.  
  892.   { writePart7 /OtherPageLengths exch def } totemp
  893.   /OtherPageTempEnd exch def /OtherPageTempStart exch def
  894.   (Wrote other pages) now
  895.  
  896.     % Write the shared objects for other pages (part 8).
  897.  
  898.   { writePart8 } totemp
  899.   /SharedTempEnd exch def /SharedTempStart exch def
  900.   (Wrote shared objects) now
  901.  
  902.     % Write the objects not associated with pages (part 9).
  903.  
  904.   { writePart9 } totemp
  905.   /NonPageTempEnd exch def /NonPageTempStart exch def
  906.  
  907.     % Compute conservative lengths of parts 2,3,5,11 of the output.
  908.     % It's OK for these to be too large, but not too small.
  909.  
  910.   % Make dummy XRef entres for LPDict and PHS.
  911.   XRef LPDictN omap 0 put
  912.   XRef PHSN omap 0 put
  913.  
  914.   /HeaderLength {    % this is exact
  915.     writePart1            % part 1
  916.   } tolength def
  917.   /CatalogLength    % this is exact
  918.     CatalogTempEnd CatalogTempStart sub def    % part 4
  919.   /FirstPageLength    % this is exact
  920.     FirstPageTempEnd FirstPageTempStart sub def    % part 6
  921.   /OtherObjectsLength    % this is exact
  922.     NonPageTempEnd OtherPageTempStart sub def    % parts 7,8,9
  923.   /ObjectsLength    % this is exact
  924.     CatalogLength FirstPageLength add OtherObjectsLength add def
  925.   /XrefLength {            % part 11
  926.     % The LPDict must end within the first 1024 bytes,
  927.     % so the start of the FirstPage xref table can't exceed 1024.
  928.     writePart11xref 1024 writePart11rest
  929.   } tolength def
  930.   /NominalFileLength     % Make a generous allowance for parts 2,3,5.
  931.     HeaderLength ObjectsLength 3 mul add 10000 add 99999 max def
  932.   /FirstPageXrefLength {    % part 3
  933.     NominalFileLength writePart3
  934.   } tolength def
  935.   /LPDictLength {        % part 2
  936.     NominalFileLength dup dup 2 mul 1 index dup createLPDict writePart2
  937.   } tolength def
  938.  
  939.     % Compute a few additional values from the above.
  940.  
  941.   /XrefBeginLength {
  942.     (xref\n0 ) ows
  943.     OFile FirstPageXN write=
  944.   } tolength def
  945.   HeaderLength LPDictLength add
  946.   /FirstPageXrefStart 1 index def
  947.   FirstPageXrefLength add
  948.   /CatalogStart 1 index def
  949.   CatalogLength add        % phsstart
  950.   /PHSStart exch def
  951.  
  952.     % Adjust the object positions ignoring PHS.
  953.     % (Writing the PHS needs these.)
  954.  
  955.   0 0 CatalogStart CatalogTempStart sub adjustObjectPositions
  956.   % Make a temporary XRef entry for the PHS, for the benefit of omend.
  957.   XRef PHSN omap CatalogStart put
  958.   (Adjusted positions) now
  959.  
  960.     % Construct the hint tables (part 5).
  961.  
  962.   { writePageOffsetHints } totemp
  963.   pop /PHSTempStart exch def
  964.   { writeSharedObjectHints } totemp
  965.   exch PHSTempStart sub PHS /S 3 -1 roll put
  966.   PHSTempStart sub /PHSTempLength exch def
  967.   (Wrote hints) now
  968.  
  969.   % Prepare to read TFile.
  970.   TFile closefile
  971.   /TFile TFileName (r) file def
  972.  
  973.   PHS
  974.     dup /File TFile put
  975.     dup /FilePosition PHSTempStart put
  976.     dup /Length PHSTempLength put
  977.   pop
  978.   /PHSLength { writePart5 } tolength def
  979.  
  980.     % Construct the linearization parameter dictionary (part 2).
  981.  
  982.   PHSStart
  983.   dup PHSLength add        % phsend
  984.   /FirstPageStart 1 index def
  985.   dup FirstPageLength add    % firstpageend
  986.   dup OtherObjectsLength add
  987.   /XrefStart 1 index def
  988.   XrefBeginLength add        % xref0start
  989.   dup XrefBeginLength sub XrefLength add    % fileend
  990.   /FileLength 1 index def
  991.   createLPDict
  992.  
  993.     % Adjust the object positions again, taking the PHS into account.
  994.  
  995.   PHSStart 0 PHSLength adjustObjectPositions
  996.   (Readjusted positions) now
  997.  
  998.     % Finally, write the output file.
  999.  
  1000.   writePart1
  1001.   writePart2
  1002.   FirstPageXrefStart padto
  1003.   XrefStart writePart3
  1004.   CatalogStart padto
  1005.   CatalogTempStart CatalogTempEnd copyrange    % part 4
  1006.   writePart5
  1007.   FirstPageStart padto
  1008.   FirstPageTempStart NonPageTempEnd copyrange    % parts 6,7,8,9
  1009.   % No Overflow Hint Stream (part 10).
  1010.   XrefStart padto
  1011.   writePart11xref
  1012.   { FirstPageXrefStart writePart11rest } tomemory
  1013.   FileLength 1 index length sub padto ows
  1014.   (Wrote output file) now
  1015.  
  1016.     % Wrap up.
  1017.  
  1018.   TFile closefile TFileName deletefile
  1019.   end        % temporary dict
  1020.   end        % IDict
  1021. } bind def
  1022.  
  1023. end            % pdfoptdict
  1024. .setglobal
  1025.  
  1026. % Check for command line arguments.
  1027. [ shellarguments {
  1028.   ] dup length 2 eq {
  1029.     % Load the pdfwrite utilities if necessary.
  1030.     /omapinit where { pop } { (pdfwrite.ps) runlibfile } ifelse
  1031.     save exch
  1032.     aload pop exch (r) file exch (w) file
  1033.     3000000 setvmthreshold
  1034.     pdfoptdict begin pdfOptimize end
  1035.     restore
  1036.   } {
  1037.     (Usage: gs -dNODISPLAY -- pdfopt.ps input.pdf output.pdf) = flush quit
  1038.   } ifelse
  1039. } {
  1040.   pop
  1041. } ifelse
  1042.